#include <cstdio>
#include <vector>

#include <iostream>

#include <camera.hpp>
#include <lightsourceRenderer.hpp>

struct LightBase {
    glm::vec3 clrAmbient;
    glm::vec3 clrDiffuse;
    glm::vec3 clrSpecular;
};

struct DirLight : LightBase {
    glm::vec3 direction;
};

struct PtLight : LightBase{
    glm::vec3 position;
    float constant;
    float linear;
    float quadratic;
};

struct SpotLight : LightBase{
    glm::vec3 direction;
    float innerCutoff;
    float outterCutoff;
};

struct MatInfo {
    int diffuseMap;
    int specularMap;
    float shininess;
};

class L12Renderer {
private:
    char buffer[32];
    Shader shaderObj;

public:
    L12Renderer(std::string bp);
    bool ready();
    void use();
    void setCameraInfo(CamInfo &camInfo);
    void setMaterialInfo(MatInfo &matInfo);
    void setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat);

    void setDirLight( DirLight &dirLights );
    void setPtLights( PtLight* ptLights, unsigned int size );
    void setSpotLight( SpotLight &SpotLights );
};

L12Renderer::L12Renderer(std::string bp) : shaderObj( (bp + "\\lesson12_vs.glsl").c_str(), (bp + "\\lesson12_fs.glsl").c_str() ){}
bool L12Renderer::ready()
{
    return shaderObj.ready();
}
void L12Renderer::use()
{
    shaderObj.use();
}
void L12Renderer::setCameraInfo(CamInfo &camInfo)
{
    shaderObj.setVec3("viewPos", camInfo.camPos);
    shaderObj.setMat4("viewMat", camInfo.view);
    shaderObj.setMat4("projMat", camInfo.projection);
}
void L12Renderer::setMaterialInfo(MatInfo &matInfo)
{
    shaderObj.setInt("material.diffuse", matInfo.diffuseMap);
    shaderObj.setInt("material.specular", matInfo.specularMap);
    shaderObj.setFloat("material.shininess", matInfo.shininess);
}
void L12Renderer::setModelMat(glm::mat4 &modleMat, glm::mat3 &normalMat)
{
    shaderObj.setMat4("modelMat", modleMat);
    shaderObj.setMat3("normalMat", normalMat);
}
void L12Renderer::setDirLight(DirLight &dirLights)
{
    shaderObj.setVec3("dirLight.ambient", dirLights.clrAmbient);
    shaderObj.setVec3("dirLight.diffuse", dirLights.clrDiffuse);
    shaderObj.setVec3("dirLight.specular", dirLights.clrSpecular);
    shaderObj.setVec3("dirLight.direction", dirLights.direction);
}
void L12Renderer::setSpotLight(SpotLight &spotLight)
{
    shaderObj.setVec3("spotLight.direction", spotLight.direction);
    shaderObj.setVec3("spotLight.ambient", spotLight.clrAmbient);
    shaderObj.setVec3("spotLight.diffuse", spotLight.clrDiffuse);
    shaderObj.setVec3("spotLight.specular", spotLight.clrSpecular);

    shaderObj.setFloat("spotLight.innerCutoff", spotLight.innerCutoff);
    shaderObj.setFloat("spotLight.outterCutoff", spotLight.outterCutoff);
}
void L12Renderer::setPtLights( PtLight* ptLights, unsigned int size )
{
    for(unsigned int i = 0; i < size; i++)
    {
        snprintf(buffer, 32, "ptLight[%d].position", i);
        shaderObj.setVec3(buffer, ptLights[i].position);
        snprintf(buffer, 32, "ptLight[%d].ambient", i);
        shaderObj.setVec3(buffer, ptLights[i].clrAmbient);
        snprintf(buffer, 32, "ptLight[%d].diffuse", i);
        shaderObj.setVec3(buffer, ptLights[i].clrDiffuse);
        snprintf(buffer, 32, "ptLight[%d].specular", i);
        shaderObj.setVec3(buffer, ptLights[i].clrSpecular);

        snprintf(buffer, 32, "ptLight[%d].constant", i);
        shaderObj.setFloat(buffer, ptLights[i].constant);
        snprintf(buffer, 32, "ptLight[%d].linear", i);
        shaderObj.setFloat(buffer, ptLights[i].linear);
        snprintf(buffer, 32, "ptLight[%d].quadratic", i);
        shaderObj.setFloat(buffer, ptLights[i].quadratic);
    }
}